package com.thitech.sif.context.parser;

import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.dom4j.Document;
import org.dom4j.DocumentException;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;

import com.thitech.sif.Constant;
import com.thitech.sif.context.ApplicationContextFactory;
import com.thitech.sif.context.IContextFactory;
import com.thitech.sif.context.SocketContext;
import com.thitech.sif.context.WSContext;
import com.thitech.sif.exception.SifException;
import com.thitech.sif.interceptor.InterceptorFactory;
import com.thitech.sif.interceptor.InterceptorHandler;
import com.thitech.sif.utils.IOUtils;
import com.thitech.sif.utils.StringUtils;
import com.thitech.sif.utils.log.LogFactory;
import com.thitech.sif.utils.log.SifLogger;
import com.thitech.sif.utils.xml.XMLUtil;
import com.thitech.sif.utils.xml.XmlValidate;
import com.thitech.sif.utils.xml.XsdXmlValidate;

public class XmlContextParser implements IContextParser {
	private SifLogger logger = LogFactory.getLogger(getClass());
	
	private static final String SYSTEM_NAME = "system-name";
	private static final String SERVICE_NAME = "service-name";
	private static final String HOST = "host";
	private static final String PORT = "port";
	private static final String CTO = "cto";
	private static final String RTO = "rto";
	private static final String CHARSET = "charset";
	private static final String USER_NAME = "user-name";
	private static final String PASSWORD = "password";
	private static final String RETURN_ASSERT_CLASS = "return-assert-class";
	private static final String INTERCEPTORS = "interceptors";
	private static final String INTERCEPTOR = "interceptor";
	private static final String PROTOCOL = "protocol";
	private static final String PORT_NAME = "port-name";
	private static final String LOCATOR_CLASS = "locator-class";
	private static final String QUERY_STRING = "query-string";
	private static final String WS_SERVICE = "ws-service";
	private static final String WS_LOCATOR = "ws-locator";
	private static final String SOCKET_SERVICE = "socket-service";
	private static final String SYSTEM = "system";
	private static final String LOCATOR = "locator";
	private static final String ID = "id";
	private static final String REF = "ref";
	private static final String CLASS = "class";
	private static final String USE = "use";
	
	private static final int DEFAULT_PORT = -1;
	private static final int DEFAULT_CTO = 60000;
	private static final int DEFAULT_RTO = 60000;
	private static final String DEFAULT_CHARSET = "UTF-8";
	private static final String DEFAULT_PROTOCOL = "http";
	
	private static final String XSD_FILE_LOCATION = "/sif-1.0.xsd";
	private static final String XSD_PREFIX = "xsd";
	private static final String XSD_NS = "http://www.thitech.com/ns/sif";
	
	
	private String xmlPath;
	private boolean validate = true;
	private IContextFactory ctxFactory;
	
	public String getXmlPath() {
		return xmlPath;
	}

	public void setXmlPath(String xmlPath) {
		this.xmlPath = xmlPath;
	}
	
	public boolean isValidate() {
		return validate;
	}

	public void setValidate(boolean validate) {
		this.validate = validate;
	}
	
	public XmlContextParser(String xmlPath, boolean validate) {
		this.xmlPath = xmlPath;
		this.validate = validate;
		this.ctxFactory = ApplicationContextFactory.getInstances();
	}

	public XmlContextParser(String xmlPath) {
		this(xmlPath, true);
	}

	@Override
	public void parser() {
		if (isValidate()) {
			XmlValidate validate = new XsdXmlValidate(IOUtils.loadFileInputStream(getXmlPath()), XSD_FILE_LOCATION);
			validate.validateXML();
			logger.debug("xml：" + getXmlPath() + ", 文件校验通过!");
		}

		Element rootElement = XMLUtil.getRootElement(getXmlContextDocument());
		if (null == rootElement) {
			throw new SifException("配置文件中获取根节点为空！");
		}
		
		parserWsElement(rootElement);
		parserSocketElement(rootElement);
	}

	/**
	 * 解析Web Service
	 * @param rootElement
	 */
	private void parserWsElement(Element rootElement) {
		logger.debug("------------------------------WS-SERVICE解析开始------------------------------");
		for (Iterator<?> i = rootElement.elementIterator(WS_SERVICE); i.hasNext();) {
			Element element = (Element) i.next();
			String id = XMLUtil.getAttributeAsString(element, ID, false);
			logger.debug("--解析ID为：" + id + "，名为：" + element.getName() + "的element开始...");
			
			String serviceId = Constant.SERVICE_TYPE_WS + "_" + id;
			logger.debug("----判断" + serviceId + "在Context是否存在");
			boolean exist = ctxFactory.isExist(serviceId);
			if (exist) {
				throw new SifException("ApplicationContext中存在与" + serviceId + "相同的Context对象");
			}
			
			logger.debug("----查找对应的System");
			Element serviceSystemEle = XMLUtil.getFirstChild(element, SYSTEM, false);
			String systemId = XMLUtil.getAttributeAsString(serviceSystemEle, REF, false);
			String systemXpath = XSD_PREFIX + ":" + SYSTEM + "[@id='" + systemId + "']";
			logger.debug("----查找对应的System，XPath＝" + systemXpath);
			List<?> systemNodes = rootElement.selectNodes(systemXpath);
			if (null == systemNodes || 0 == systemNodes.size()) {
				throw new SifException("没有找到ID为" + systemId  + "的system节点");
			} else if (systemNodes.size() > 1) {
				throw new SifException("ID为" + systemId  + "的system节点不是唯一的node");
			}
			
			//system节点中的全局数据
			logger.debug("----从System节点中获取全局数据");
			Element systemElement = (Element) systemNodes.get(0);
			String systemName = XMLUtil.getContentAsString(XMLUtil.getFirstChild(systemElement, SYSTEM_NAME, false), false);
			String host = XMLUtil.getContentAsString(XMLUtil.getFirstChild(systemElement, HOST, false), false);
			int port = XMLUtil.getContentAsInt(XMLUtil.getFirstChild(systemElement, PORT, true), DEFAULT_PORT, true);
			String charset = XMLUtil.getContentAsString(XMLUtil.getFirstChild(systemElement, CHARSET, true), true);
			charset = (null == charset || "".equals(charset)) ? DEFAULT_CHARSET : charset;
			
			//Service Name是ws-service特有的节点
			logger.debug("----从ws-service节点中获取Service Name值");
			String serviceName = XMLUtil.getContentAsString(XMLUtil.getFirstChild(element, SERVICE_NAME, false), false);
			
			//连接超时时间
			logger.debug("----获取CTO值");
			Element ctoEle = XMLUtil.getFirstChild(element, CTO, true);
			if (null == ctoEle) {
				ctoEle = XMLUtil.getFirstChild(systemElement, CTO, true);
			}
			int cto = XMLUtil.getContentAsInt(ctoEle, DEFAULT_CTO, true);
			
			//读取超时时间
			logger.debug("----获取RTO值");
			Element rtoEle = XMLUtil.getFirstChild(element, RTO, true);
			if (null == rtoEle) {
				rtoEle = XMLUtil.getFirstChild(systemElement, RTO, true);
			}
			int rto = XMLUtil.getContentAsInt(rtoEle, DEFAULT_RTO, true);
			
			//用户名
			logger.debug("----获取User Name值");
			Element userNameEle = XMLUtil.getFirstChild(element, USER_NAME, true);
			if (null == userNameEle) {
				userNameEle = XMLUtil.getFirstChild(systemElement, USER_NAME, true);
			}
			String userName = XMLUtil.getContentAsString(userNameEle, true);
			
			//密码
			logger.debug("----获取Password值");
			Element passwordEle = XMLUtil.getFirstChild(element, PASSWORD, true);
			if (null == passwordEle) {
				passwordEle = XMLUtil.getFirstChild(systemElement, PASSWORD, true);
			}
			String password = XMLUtil.getContentAsString(passwordEle, true);
			
			//返回值判断类路径
			logger.debug("----获取Return assert class值");
			Element retAssertClassEle = XMLUtil.getFirstChild(element, RETURN_ASSERT_CLASS, true);
			if (null == retAssertClassEle) {
				retAssertClassEle = XMLUtil.getFirstChild(systemElement, RETURN_ASSERT_CLASS, false);
			}
			String retAssertClass = XMLUtil.getContentAsString(retAssertClassEle, false);
			
			//通信协议
			logger.debug("----获取protocol值");
			Element protocolEle = XMLUtil.getFirstChild(element, PROTOCOL, true);
			if (null == protocolEle) {
				protocolEle = XMLUtil.getFirstChild(systemElement, PROTOCOL, true);
			}
			String protocol = XMLUtil.getContentAsString(protocolEle, true);
			protocol = (null == protocol || "".equals(protocol)) ? DEFAULT_PROTOCOL : protocol;
			
			//拦截器
			logger.debug("----获取拦截器");
			Element interceptorsEle = XMLUtil.getFirstChild(element, INTERCEPTORS, false);
			if (!interceptorsEle.elementIterator(INTERCEPTOR).hasNext()) {
				String interceptorId = XMLUtil.getAttributeAsString(interceptorsEle, REF, false);
				String interceptorXpath = XSD_PREFIX + ":" + INTERCEPTORS + "[@id='" + interceptorId + "']";
				List<?> interceptorNodes = rootElement.selectNodes(interceptorXpath);
				if (null == interceptorNodes || 0 == interceptorNodes.size()) {
					throw new SifException("没有找到ID为" + interceptorId  + "的interceptors节点");
				} else if (systemNodes.size() > 1) {
					throw new SifException("ID为" + interceptorId  + "的interceptors节点不是唯一的node");
				}
				interceptorsEle = (Element) interceptorNodes.get(0);
			}
			
			List<InterceptorHandler> interceptors = new ArrayList<InterceptorHandler>();
			interceptors.addAll(InterceptorFactory.getBeforeDefaultInterceptors(Constant.SERVICE_TYPE_WS));
			for (Iterator<?> j = interceptorsEle.elementIterator(INTERCEPTOR); j.hasNext();) {
				Element interceptor = (Element) j.next();
				String interceptorCls = XMLUtil.getAttributeAsString(interceptor, USE, true);
				if (StringUtils.isNull(interceptorCls)) {
					interceptorCls = XMLUtil.getAttributeAsString(interceptor, CLASS, false);
					try {
						Class<?> cls = Class.forName(interceptorCls);
						interceptors.add((InterceptorHandler) cls.newInstance());
					} catch (Exception e) {
						throw new SifException("根据" + interceptorCls + "实例化拦截器失败", e);
					}
				} else {
					InterceptorHandler inter = InterceptorFactory.getInterceptor(interceptorCls);
					interceptors.add(inter);
				}
			}
			interceptors.addAll(InterceptorFactory.getAfterDefaultInterceptors(Constant.SERVICE_TYPE_WS));
			
			
			//Web Service locator配置项目
			logger.debug("----获取locator值");
			Element locatorEle = XMLUtil.getFirstChild(element, LOCATOR, false);
			String portName = XMLUtil.getContentAsString(XMLUtil.getFirstChild(locatorEle, PORT_NAME, true), true);
			String locatorCls = XMLUtil.getContentAsString(XMLUtil.getFirstChild(locatorEle, LOCATOR_CLASS, true), true);
			String queryString = XMLUtil.getContentAsString(XMLUtil.getFirstChild(locatorEle, QUERY_STRING, true), true);
			
			Element wsLocatorElement = null;
			if (StringUtils.isNull(portName) || StringUtils.isNull(locatorCls) || StringUtils.isNull(queryString)) {
				String locatorId = XMLUtil.getAttributeAsString(locatorEle, REF, false);
				String locatorXpath = XSD_PREFIX + ":" + WS_LOCATOR + "[@id='" + locatorId + "']";
				List<?> wsLocatorNodes = rootElement.selectNodes(locatorXpath);
				if (null == wsLocatorNodes || 0 == wsLocatorNodes.size()) {
					throw new SifException("没有找到ID为" + locatorId  + "的ws-locator节点");
				} else if (systemNodes.size() > 1) {
					throw new SifException("ID为" + locatorId  + "的ws-locator节点不是唯一的node");
				}
				wsLocatorElement = (Element) wsLocatorNodes.get(0);
			}
			
			if (StringUtils.isNull(portName)) {
				portName = XMLUtil.getContentAsString(XMLUtil.getFirstChild(wsLocatorElement, PORT_NAME, false), false);
			}
			
			if (StringUtils.isNull(locatorCls)) {
				locatorCls = XMLUtil.getContentAsString(XMLUtil.getFirstChild(wsLocatorElement, LOCATOR_CLASS, false), false);
			}
			
			if (StringUtils.isNull(queryString)) {
				queryString = XMLUtil.getContentAsString(XMLUtil.getFirstChild(wsLocatorElement, QUERY_STRING, false), false);
			}

			//xml数据解析完成，开始放入Application Context中
			logger.debug("----把从xml中获取的值配置到Application Context中");
			WSContext wsCtx = ctxFactory.createWSContext();
			wsCtx.setCharset(charset);
			wsCtx.setCto(cto);
			wsCtx.setRto(rto);
			wsCtx.setHost(host);
			wsCtx.setLocatorClass(locatorCls);
			wsCtx.setPassword(password);
			wsCtx.setPort(port);
			wsCtx.setPortName(portName);
			wsCtx.setProtocol(protocol);
			wsCtx.setQueryString(queryString);
			wsCtx.setReturnAssertClass(retAssertClass);
			wsCtx.setServiceName(serviceName);
			wsCtx.setSystemName(systemName);
			wsCtx.setUserName(userName);
			wsCtx.getInterceptors().addAll(interceptors);
			ctxFactory.putContext(serviceId, wsCtx);
			
			logger.debug("--解析ID为：" + id + "，名为：" + element.getName() + "的element结束...");
		}
		logger.debug("------------------------------WS-SERVICE解析结束------------------------------");
	}
	
	/**
	 * 解析Socket
	 * @param rootElement
	 */
	private void parserSocketElement(Element rootElement) {
		logger.debug("------------------------------SOCKET解析开始------------------------------");
		for (Iterator<?> i = rootElement.elementIterator(SOCKET_SERVICE); i.hasNext();) {
			Element element = (Element) i.next();
			String id = XMLUtil.getAttributeAsString(element, ID, false);
			logger.debug("--解析ID为：" + id + "，名为：" + element.getName() + "的element开始...");
			
			String serviceId = Constant.SERVICE_TYPE_SOCKET + "_" + id;
			logger.debug("----判断" + serviceId + "在Context是否存在");
			boolean exist = ctxFactory.isExist(serviceId);
			if (exist) {
				throw new SifException("ApplicationContext中存在与" + serviceId + "相同的Context对象");
			}
			
			logger.debug("----查找对应的System");
			Element serviceSystemEle = XMLUtil.getFirstChild(element, SYSTEM, false);
			String systemId = XMLUtil.getAttributeAsString(serviceSystemEle, REF, false);
			String systemXpath = XSD_PREFIX + ":" + SYSTEM + "[@id='" + systemId + "']";
			logger.debug("----查找对应的System，XPath＝" + systemXpath);
			List<?> systemNodes = rootElement.selectNodes(systemXpath);
			if (null == systemNodes || 0 == systemNodes.size()) {
				throw new SifException("没有找到ID为" + systemId  + "的system节点");
			} else if (systemNodes.size() > 1) {
				throw new SifException("ID为" + systemId  + "的system节点不是唯一的node");
			}
			
			//system节点中的全局数据
			logger.debug("----从System节点中获取全局数据");
			Element systemElement = (Element) systemNodes.get(0);
			String systemName = XMLUtil.getContentAsString(XMLUtil.getFirstChild(systemElement, SYSTEM_NAME, false), false);
			String host = XMLUtil.getContentAsString(XMLUtil.getFirstChild(systemElement, HOST, false), false);
			int port = XMLUtil.getContentAsInt(XMLUtil.getFirstChild(systemElement, PORT, true), DEFAULT_PORT, true);
			String charset = XMLUtil.getContentAsString(XMLUtil.getFirstChild(systemElement, CHARSET, true), true);
			charset = (null == charset || "".equals(charset)) ? DEFAULT_CHARSET : charset;
			
			//Service Name是socket-service特有的节点
			logger.debug("----从socket-service节点中获取Service Name值");
			String serviceName = XMLUtil.getContentAsString(XMLUtil.getFirstChild(element, SERVICE_NAME, false), false);
			
			//连接超时时间
			logger.debug("----获取CTO值");
			Element ctoEle = XMLUtil.getFirstChild(element, CTO, true);
			if (null == ctoEle) {
				ctoEle = XMLUtil.getFirstChild(systemElement, CTO, true);
			}
			int cto = XMLUtil.getContentAsInt(ctoEle, DEFAULT_CTO, true);
			
			//读取超时时间
			logger.debug("----获取RTO值");
			Element rtoEle = XMLUtil.getFirstChild(element, RTO, true);
			if (null == rtoEle) {
				rtoEle = XMLUtil.getFirstChild(systemElement, RTO, true);
			}
			int rto = XMLUtil.getContentAsInt(rtoEle, DEFAULT_RTO, true);
			
			//用户名
			logger.debug("----获取User Name值");
			Element userNameEle = XMLUtil.getFirstChild(element, USER_NAME, true);
			if (null == userNameEle) {
				userNameEle = XMLUtil.getFirstChild(systemElement, USER_NAME, true);
			}
			String userName = XMLUtil.getContentAsString(userNameEle, true);
			
			//密码
			logger.debug("----获取Password值");
			Element passwordEle = XMLUtil.getFirstChild(element, PASSWORD, true);
			if (null == passwordEle) {
				passwordEle = XMLUtil.getFirstChild(systemElement, PASSWORD, true);
			}
			String password = XMLUtil.getContentAsString(passwordEle, true);
			
			//返回值判断类路径
			logger.debug("----获取Return assert class值");
			Element retAssertClassEle = XMLUtil.getFirstChild(element, RETURN_ASSERT_CLASS, true);
			if (null == retAssertClassEle) {
				retAssertClassEle = XMLUtil.getFirstChild(systemElement, RETURN_ASSERT_CLASS, false);
			}
			String retAssertClass = XMLUtil.getContentAsString(retAssertClassEle, false);
			
			//拦截器
			logger.debug("----获取拦截器");
			Element interceptorsEle = XMLUtil.getFirstChild(element, INTERCEPTORS, false);
			if (!interceptorsEle.elementIterator(INTERCEPTOR).hasNext()) {
				String interceptorId = XMLUtil.getAttributeAsString(interceptorsEle, REF, false);
				String interceptorXpath = XSD_PREFIX + ":" + INTERCEPTORS + "[@id='" + interceptorId + "']";
				List<?> interceptorNodes = rootElement.selectNodes(interceptorXpath);
				if (null == interceptorNodes || 0 == interceptorNodes.size()) {
					throw new SifException("没有找到ID为" + interceptorId  + "的interceptors节点");
				} else if (systemNodes.size() > 1) {
					throw new SifException("ID为" + interceptorId  + "的interceptors节点不是唯一的node");
				}
				interceptorsEle = (Element) interceptorNodes.get(0);
			}
			
			List<InterceptorHandler> interceptors = new ArrayList<InterceptorHandler>();
			interceptors.addAll(InterceptorFactory.getBeforeDefaultInterceptors(Constant.SERVICE_TYPE_SOCKET));
			for (Iterator<?> j = interceptorsEle.elementIterator(INTERCEPTOR); j.hasNext();) {
				Element interceptor = (Element) j.next();
				String interceptorCls = XMLUtil.getAttributeAsString(interceptor, USE, true);
				if (StringUtils.isNull(interceptorCls)) {
					interceptorCls = XMLUtil.getAttributeAsString(interceptor, CLASS, false);
					try {
						Class<?> cls = Class.forName(interceptorCls);
						interceptors.add((InterceptorHandler) cls.newInstance());
					} catch (Exception e) {
						throw new SifException("根据" + interceptorCls + "实例化拦截器失败", e);
					}
				} else {
					InterceptorHandler inter = InterceptorFactory.getInterceptor(interceptorCls);
					interceptors.add(inter);
				}
			}
			interceptors.addAll(InterceptorFactory.getAfterDefaultInterceptors(Constant.SERVICE_TYPE_SOCKET));

			//xml数据解析完成，开始放入Application Context中
			logger.debug("----把从xml中获取的值配置到Application Context中");
			SocketContext socketCtx = ctxFactory.createSocketContext();
			socketCtx.setCharset(charset);
			socketCtx.setCto(cto);
			socketCtx.setRto(rto);
			socketCtx.setHost(host);
			socketCtx.setPassword(password);
			socketCtx.setPort(port);
			socketCtx.setReturnAssertClass(retAssertClass);
			socketCtx.setServiceName(serviceName);
			socketCtx.setSystemName(systemName);
			socketCtx.setUserName(userName);
			socketCtx.getInterceptors().addAll(interceptors);
			ctxFactory.putContext(serviceId, socketCtx);
			
			logger.debug("--解析ID为：" + id + "，名为：" + element.getName() + "的element结束...");
		}
		logger.debug("------------------------------SOCKET解析结束------------------------------");
	}

	
	private Document getXmlContextDocument() {
		if (null == getXmlPath()) {
			throw new SifException("xml path is null");
		}
		
		//InputStream in = this.getClass().getClassLoader().getResourceAsStream(getXmlPath());
		InputStream in = IOUtils.loadFileInputStream(getXmlPath());
		SAXReader reader = new SAXReader();
		Document document = null;
		try {
			Map<String, String> map = new HashMap<String, String>(); 
			map.put (XSD_PREFIX, XSD_NS);
			reader.getDocumentFactory().setXPathNamespaceURIs(map);
			document = reader.read(in);
		} catch (DocumentException e) {
			throw new SifException("读取配置文件异常，xmlPath=" + getXmlPath(), e);
		} finally {
			IOUtils.closeQuietly(in);
		}
		return document;
	}
	

	
	public static void main(String[] args) {
		IContextParser paser = new XmlContextParser("http://localhost:8081/fileWeb/service.xml", true);
		paser.parser();
		
		WSContext ctx = ApplicationContextFactory.getInstances().getWSContext(Constant.SERVICE_TYPE_WS + "_TEST_SERVICE_ID1");
		ctx.getHost();
	}
}
